package de.westnordost.streetcomplete.location;

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;

import de.westnordost.streetcomplete.R;
import de.westnordost.streetcomplete.view.dialogs.AlertDialogBuilder;

import static android.location.LocationManager.PROVIDERS_CHANGED_ACTION;
import static de.westnordost.streetcomplete.location.LocationUtil.MODE_CHANGED;

/** Manages the process to ensure that the app can access the user's location. Two steps:
 *  <ol>
 *      <li>ask for permission</li>
 *      <li>ask for location to be turned on</li>
 *  </ol>
 *
 *  This fragment reports back to the Activity it is attached to via LocationRequestListener.
 *  The process is started via {@link #startRequest()} */
public class LocationRequestFragment extends Fragment
{
	private static final int LOCATION_PERMISSION_REQUEST = 1;
	private static final int LOCATION_TURN_ON_REQUEST = 2;

	public interface LocationRequestListener
	{
		void onLocationRequestFinished(LocationState withLocationState);
	}
	private LocationRequestListener callbackListener;

	private LocationState state;
	private boolean inProgress;
	private BroadcastReceiver locationProviderChangedReceiver;

	public LocationRequestFragment()
	{
		super();
		state = null;
	}

	/** Start location request process. When already started, will not be started again. */
	public void startRequest()
	{
		if(!inProgress)
		{
			inProgress = true;
			state = null;
			nextStep();
		}
	}

	private void nextStep()
	{
		if(state == null || state == LocationState.DENIED)
		{
			requestLocationPermissions();
		}
		else if(state == LocationState.ALLOWED)
		{
			requestLocationSettingsToBeOn();
		}
		else if(state == LocationState.ENABLED)
		{
			finish();
		}
	}

	private void finish()
	{
		inProgress = false;
		callbackListener.onLocationRequestFinished(state);
	}

	@Override public void onAttach(Context context) {
		super.onAttach(context);
		callbackListener = (LocationRequestListener) context;
	}

	@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
										   @NonNull int[] grantResults)
	{
		// must be for someone else...
		if(requestCode != LOCATION_PERMISSION_REQUEST) return;
		if(permissions.length == 0 || !permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION))
			return;

		if(grantResults[0] == PackageManager.PERMISSION_GRANTED)
		{
			requestLocationPermissions(); // retry then...
		}
		else
		{
			new AlertDialogBuilder(getContext())
					.setMessage(R.string.no_location_permission_warning)
					.setPositiveButton(R.string.retry,
							new DialogInterface.OnClickListener()
							{
								@Override
								public void onClick(DialogInterface dialog, int which)
								{
									requestLocationPermissions();
								}
							})
					.setNegativeButton(android.R.string.cancel,
							new DialogInterface.OnClickListener()
							{
								@Override public void onClick(DialogInterface dialog, int which)
								{
									state = LocationState.DENIED;
									finish();
								}
							})
					.setOnCancelListener(
							new DialogInterface.OnCancelListener()
							{
								@Override public void onCancel(DialogInterface dialog)
								{
									state = LocationState.DENIED;
									finish();
								}
							}
					)
					.show();
		}
	}

	@Override public void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		// must be for someone else...
		if(requestCode != LOCATION_TURN_ON_REQUEST) return;
		// we ignore the resultCode, because we always get Activity.RESULT_CANCELED. Instead, we
		// check if the conditions are fulfilled now
		requestLocationSettingsToBeOn();
	}

	private void requestLocationPermissions()
	{
		if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION)
				== PackageManager.PERMISSION_GRANTED)
		{
			state = LocationState.ALLOWED;
			nextStep();

		} else {
			requestPermissions(new String[]{ Manifest.permission.ACCESS_FINE_LOCATION },
					LOCATION_PERMISSION_REQUEST);
		}
	}

	private void requestLocationSettingsToBeOn()
	{
		if(LocationUtil.isLocationSettingsOn(getContext()))
		{
			state = LocationState.ENABLED;
			nextStep();
		}
		else
		{
			final AlertDialog dlg = new AlertDialogBuilder(getContext())
					.setMessage(R.string.turn_on_location_request)
					.setPositiveButton(android.R.string.yes,
							new DialogInterface.OnClickListener()
							{
								@Override public void onClick(DialogInterface dialog, int which)
								{
									dialog.dismiss();
									Intent viewIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
									startActivityForResult(viewIntent, LOCATION_TURN_ON_REQUEST);
								}
							})
					.setNegativeButton(android.R.string.no,
							new DialogInterface.OnClickListener()
							{
								@Override public void onClick(DialogInterface dialog, int which)
								{
									cancelTurnLocationOnDialog();
								}
							})
					.setOnCancelListener(
							new DialogInterface.OnCancelListener()
							{
								@Override public void onCancel(DialogInterface dialog)
								{
									cancelTurnLocationOnDialog();
								}
							}
					).create();

			// the user may turn on location in the pull-down-overlay, without actually going into
			// settings dialog
			registerForLocationProviderChanges(dlg);

			dlg.show();
		}
	}

	private void cancelTurnLocationOnDialog()
	{
		unregisterForLocationProviderChanges();
		finish();
	}

	private void registerForLocationProviderChanges(final AlertDialog dlg)
	{
		locationProviderChangedReceiver = new BroadcastReceiver()
		{
			@Override public void onReceive(Context context, Intent intent)
			{
				dlg.dismiss();
				unregisterForLocationProviderChanges();
				requestLocationSettingsToBeOn();
			}
		};

		String name = LocationUtil.isNewLocationApi() ? MODE_CHANGED : PROVIDERS_CHANGED_ACTION;
		getActivity().registerReceiver(locationProviderChangedReceiver, new IntentFilter(name));
	}

	private void unregisterForLocationProviderChanges()
	{
		if(locationProviderChangedReceiver != null)
		{
			getActivity().unregisterReceiver(locationProviderChangedReceiver);
			locationProviderChangedReceiver = null;
		}
	}

	@Override public void onStop()
	{
		super.onStop();
		unregisterForLocationProviderChanges();
	}

	public LocationState getState()
	{
		return state != null ? state : LocationState.DENIED;
	}

	@Override public void onSaveInstanceState(Bundle outState)
	{
		super.onSaveInstanceState(outState);
		if(state != null) outState.putString("locationState", state.name());
		outState.putBoolean("inProgress", inProgress);
	}

	@Override public void onActivityCreated(@Nullable Bundle savedInstanceState)
	{
		super.onActivityCreated(savedInstanceState);
		if(savedInstanceState != null)
		{
			String stateName = savedInstanceState.getString("locationState");
			if(stateName != null) state = LocationState.valueOf(stateName);
			inProgress = savedInstanceState.getBoolean("inProgress");
		}
	}
}
